package com.vf.cloud.server.adapter;

import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import com.jfinal.kit.JsonKit;
import com.jfinal.kit.StrKit;
import com.jfinal.plugin.activerecord.Page;
import com.vf.cloud.common.constant.Cache;
import com.vf.cloud.common.domain.Gpu;
import com.vf.cloud.common.util.R;
import com.vf.cloud.common.vo.GPUSynVo;
import com.vf.cloud.server.colony.NodeServer;
import com.vf.cloud.server.signalling.pool.CachePool;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class AdapterServer {

	private static volatile AdapterServer INSYANCE;

	private AdapterServer() {
	}

	public static AdapterServer getInstance() {
		if (null == INSYANCE) {
			synchronized (AdapterServer.class) {
				if (null == INSYANCE) {
					INSYANCE = new AdapterServer();
				}
			}
		}
		return INSYANCE;
	}

	private List<Gpu> graphics;
	private ConcurrentHashMap<String, String> GPU_RESOURCE = new ConcurrentHashMap<String, String>();

	public void init() {
		graphics = Gpu.dao.find("select * from " + Gpu.TABLE_NAME + " o where o.enabled='1'  order by o.index asc ");
		for (Gpu g : graphics) {
			for (int i = 0; i < g.getLimit(); i++) {
				GPU_RESOURCE.put(g.getIndex() + "_" + i, "");
			}
		}
		onChange();
	}

	public synchronized R<Integer> applyGPU(String key) {
		if (isRestart) {
			return R.failed("GPU重启中请稍后再试.");
		}
		if (graphics == null || graphics.size() == 0) {
			return R.failed("暂无可用的GPU资源.");
		}
		for (Gpu g : graphics) {
			for (int i = 0; i < g.getLimit(); i++) {
				String binding = GPU_RESOURCE.get(g.getIndex() + "_" + i);
				if (StrKit.isBlank(binding)) {
					GPU_RESOURCE.put(g.getIndex() + "_" + i, key);
					onChange();
					return R.ok(g.getIndex());
				}
			}
		}
		return R.failed("资源已经达上限.");
	}

	public void pages(Page<Gpu> pages) {
		for (Gpu g : pages.getList()) {
			for (int i = 0; i < g.getLimit(); i++) {
				if (GPU_RESOURCE.containsKey(g.getIndex() + "_" + i)
						&& !StrKit.isBlank(GPU_RESOURCE.get(g.getIndex() + "_" + i))) {
					g.setUsed(g.getUsed() + 1);
				}
			}
			g.setPercentage((g.getUsed() / g.getLimit()) * 100);
		}
	}

	private boolean isRestart = false;

	public void restart() {
		isRestart = true;
		Iterator<String> it = GPU_RESOURCE.keySet().iterator();
		while (it.hasNext()) {
			String key = it.next();
			String playerId = GPU_RESOURCE.get(key);
			if (!StrKit.isBlank(playerId)) {
				CachePool.killUEByPlayerId(playerId);
			}
		}
		init();
		isRestart = false;
	}

	public boolean isRestart() {
		return isRestart;
	}

	private ConcurrentHashMap<String, Date> TIME_OUT = new ConcurrentHashMap<String, Date>();

	public void check() {

//		out.write("TASKKILL /F /IM nginx.exe /T \r\n");
//		
//		if(!SignallingServer.getInstance().isRunning()) {
//			Project.dao.
//			
//		}

		if (isRestart()) {
			TIME_OUT.clear();
			return;
		}
		if (GPU_RESOURCE != null && GPU_RESOURCE.size() <= 0) {
			TIME_OUT.clear();
			return;
		}

		Iterator<String> it = GPU_RESOURCE.keySet().iterator();
		while (it.hasNext()) {
			String key = it.next();
			String playerId = GPU_RESOURCE.get(key);
			if (!StrKit.isBlank(playerId)) {
				if (CachePool.existUE(playerId)) {
					if (TIME_OUT.containsKey(playerId)) {
						TIME_OUT.remove(playerId);
						log.info(String.format("UE remove Timeout :%s", playerId));
					}
				} else {
					if (TIME_OUT.containsKey(playerId)) {
						if (new Date(System.currentTimeMillis()).getTime() > TIME_OUT.get(playerId).getTime()) {
							TIME_OUT.remove(playerId);
							CachePool.killUEByPlayerId(playerId);
							GPU_RESOURCE.put(key, "");
							log.info(String.format("UE is Timeout,Kill:%s", playerId));
							onChange();
						}
					} else {
						log.info(String.format("UE add To Timeout :%s", playerId));
						TIME_OUT.put(playerId, getLaterTime());
					}
				}
			}
		}
	}

	private static Date getLaterTime() {
		Calendar cal = Calendar.getInstance();
		cal.add(Calendar.MINUTE, 1);
		return cal.getTime();
	}

	public void reset(String playerId) {
		if (isRestart()) {
			return;
		}
		for (Gpu g : graphics) {
			for (int i = 0; i < g.getLimit(); i++) {
				String binding = GPU_RESOURCE.get(g.getIndex() + "_" + i);
				if (!StrKit.isBlank(binding) && StrKit.equals(binding, playerId)) {
					GPU_RESOURCE.put(g.getIndex() + "_" + i, "");
				}
			}
		}
		onChange();
	}

	public boolean hasGPU(String playerId) {
		if (isRestart) {
			return false;
		}
		for (Gpu g : graphics) {
			for (int i = 0; i < g.getLimit(); i++) {
				String binding = GPU_RESOURCE.get(g.getIndex() + "_" + i);
				if (!StrKit.isBlank(binding) && StrKit.equals(binding, playerId)) {
					return true;
				}
			}
		}
		return false;
	}

	public void onChange() {
		log.info(String.format("GPU资源:%s", JsonKit.toJson(GPU_RESOURCE)));
		if (Cache.config != null && StrKit.equals("1", Cache.config.getEnabled())) {

			GPUSynVo vo = new GPUSynVo();

			vo.setKey(Cache.config.getLocalPwd());
			vo.setIp(Cache.config.getLocalIp());
			vo.setPort(Integer.parseInt(Cache.config.getLocalPort()));
			vo.setGpu(graphics.size());

			int limit = 0;
			int free = 0;
			int used = 0;

			for (Gpu g : graphics) {
				for (int i = 0; i < g.getLimit(); i++) {
					String binding = GPU_RESOURCE.get(g.getIndex() + "_" + i);
					if (StrKit.isBlank(GPU_RESOURCE.get(binding))) {
						free += 1;
					} else {
						used += 1;
					}
				}
				limit += g.getLimit();
			}

			vo.setLimit(limit);
			vo.setUsed(used);
			vo.setFree(free);

			Map<String, Object> GPUConfig = new HashMap<String, Object>();
			GPUConfig.put("type", "GPU_RESOURCE");
			GPUConfig.put("data", vo);
			GPUConfig.put("time", System.currentTimeMillis());
			NodeServer.getInstance().send(GPUConfig);

		}

	}

}
